#include <string>
#include <vector>

#include <boost/format.hpp>

#include <Windows.h>

#include "Exception.h"

class Exception::Private {
public:
	Private( const std::wstring & msg, const char * file, unsigned long line );

	void mkDpt();

	std::wstring msg;
	std::string aDpt;
	const char * file;
	unsigned long line;
};

Exception::Private::Private( const std::wstring & msg, const char * file, unsigned long line ):
msg( msg ),
aDpt(),
file( file ),
line( line ) {
}

void Exception::Private::mkDpt() {
	boost::format tpl( "%1% (at %2%:%3%)" );
	std::vector< char > buffer( this->msg.size() * 4 + 1 );
	auto e = WideCharToMultiByte( CP_UTF8, 0, this->msg.c_str(), -1, &buffer[0], buffer.size(), NULL, NULL );
	this->aDpt = ( tpl % &buffer[0] % this->file % this->line ).str();
}

Exception::Exception( int code, const char * file, unsigned long line ):
std::exception(),
p_( new Private( std::wstring(), file, line ) ) {
	wchar_t buffer[4096];
	auto e = _wcserror_s( buffer, code );
	this->p_->msg = buffer;

	this->p_->mkDpt();
}

Exception::Exception( const char * message, const char * file, unsigned long line ):
std::exception(),
p_( new Private( std::wstring(), file, line ) ) {
	std::vector< wchar_t > buffer( strlen( message ) + 1 );
	auto e = MultiByteToWideChar( CP_UTF8, 0, message, -1, &buffer[0], buffer.size() );
	this->p_->msg = &buffer[0];

	this->p_->mkDpt();
}

Exception::Exception( const char * message, int n, const char * file, unsigned long line ):
std::exception(),
p_( new Private( std::wstring(), file, line ) ) {
	std::vector< wchar_t > buffer( strlen( message ) + 1 );
	auto e = MultiByteToWideChar( CP_UTF8, 0, message, n, &buffer[0], buffer.size() );
	this->p_->msg = &buffer[0];

	this->p_->mkDpt();
}

Exception::Exception( const std::string & message, const char * file, unsigned long line ):
std::exception(),
p_( new Private( std::wstring(), file, line ) ) {
	std::vector< wchar_t > buffer( message.size() + 1 );
	auto e = MultiByteToWideChar( CP_UTF8, 0, message.c_str(), -1, &buffer[0], buffer.size() );
	this->p_->msg = &buffer[0];

	this->p_->mkDpt();
}

Exception::Exception( const wchar_t * message, const char * file, unsigned long line ):
std::exception(),
p_( new Private( message, file, line ) ) {
	this->p_->mkDpt();
}

Exception::Exception( const wchar_t * message, int n, const char * file, unsigned long line ):
std::exception(),
p_( new Private( std::wstring( message, n ), file, line ) ) {
	this->p_->mkDpt();
}

Exception::Exception( const std::wstring & message, const char * file, unsigned long line ):
std::exception(),
p_( new Private( message, file, line ) ) {
	this->p_->mkDpt();
}

Exception::~Exception() {
}

const char * Exception::what() const {
	return this->p_->aDpt.c_str();
}

const std::wstring & Exception::getMessage() const {
	return this->p_->msg;
}

const char * Exception::getFile() const {
	return this->p_->file;
}

unsigned long Exception::getLine() const {
	return this->p_->line;
}
